209. 长度最小的子数组

209. 长度最小的子数组

Similar Question

leading to the advanced question

Solution Tips

方案一: 滑动窗口

var minSubArrayLen = function(target, nums) {
    // 因为都是整数, 所以只需要动态的调整滑动窗口的大小就可以了
    // 小了就 end++, 大了就 start--
    // 依然是使用 sum 维护窗口即可

    let start = 0;
    let end = 0;
    let minLen = Number.MAX_SAFE_INTEGER;

    let sum = nums[0];

    while (end < nums.length) {
        if (sum < target) {
            end++;
            sum += nums[end];
        }
        else if (sum >= target) {
            minLen = Math.min(minLen, end - start);
            // start++ 这样 sum 一定变小, 下一轮就可以 end++ 继续遍历
            // end++ 一定是变大的, 所以没有意义
            sum -= nums[start];
            start++;
            // 只需要 minLen 就行, 不需要是最接近 target 的
            // 所以 > 也可以走这个分支
        }
    }
    if (start === 0) return 0;

    return minLen + 1;
};

方案二: 前缀和 + 二分查找

pPZPEkT.png

class Solution {
    public int minSubArrayLen(int s, int[] nums) {
        int n = nums.length;
        if (n == 0) {
            return 0;
        }
        int ans = Integer.MAX_VALUE;
        int[] sums = new int[n + 1]; 
        // 为了方便计算,令 size = n + 1 
        // sums[0] = 0 意味着前 0 个元素的前缀和为 0
        // sums[1] = A[0] 前 1 个元素的前缀和为 A[0]
        // 以此类推
        for (int i = 1; i <= n; i++) {
            sums[i] = sums[i - 1] + nums[i - 1];
        }
        for (int i = 1; i <= n; i++) {
            int target = s + sums[i - 1];
            int bound = Arrays.binarySearch(sums, target);
            if (bound < 0) {
                bound = -bound - 1;
            }
            if (bound <= n) {
                ans = Math.min(ans, bound - (i - 1));
            }
        }
        return ans == Integer.MAX_VALUE ? 0 : ans;
    }
}